package com.bestcem.xm.common.core.sql.calculator;

import com.bestcem.xm.common.core.constant.Constants;
import com.bestcem.xm.common.core.constant.RegTypes;
import com.bestcem.xm.common.core.sql.*;
import com.bestcem.xm.common.core.uitls.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Optional;

/**
 * @author ming.xue <ming.xue@idiaoyan.com>
 * @desc 简单条件解释器模版
 * @date 2021-05-24
 */
@Slf4j
public abstract class ConditionCalculator {
    /**
     * 简单条件代理
     */
    protected final ConditionProxy conditionProxy;

    /**
     * 计算参数代理
     */
    protected final ParamsProxy paramsProxy;

    public ConditionCalculator(ConditionProxy conditionProxy, ParamsProxy paramsProxy) {
        this.conditionProxy = conditionProxy;
        this.paramsProxy = paramsProxy;
    }

    /**
     * 获取支持的数据类型
     */
    protected abstract List<String> getSupportedDataTypes();

    /**
     * 获取操作符
     */
    protected abstract String getOperator();

    /**
     * 具体计算逻辑
     */
    protected abstract boolean onCalculate();

    /**
     * 默认返回值
     */
    protected boolean getDefaultResult() {
        return false;
    }

    /**
     * 计算
     */
    public boolean calculate() {
        String dataType = conditionProxy.getDataType();
        if (!this.isSupportedDataType(dataType)) {
            String msg = String.format("unsupported operator: %s, dataType: %s", this.getOperator(), dataType);
            throw new ConditionGrammarException(msg);
        }
        return this.onCalculate();
    }

    /**
     * 是否支持某种数据类型
     */
    protected boolean isSupportedDataType(String dataType) {
        List<String> supportedDataTypes = this.getSupportedDataTypes();
        if (supportedDataTypes == null) {
            return false;
        }

        return supportedDataTypes.contains(dataType);
    }


    public Object getParamValue() {
        Optional<String> paramValueOptional;
        // 字段名称
        String fieldName = this.conditionProxy.getName();
        // 数据类型
        String dataType = StringUtils.trimToEmpty(this.conditionProxy.getDataType());

        // 获取地址参数值
        if (StringUtils.equals(dataType, ConditionConstant.FieldTypes.ADDRESS)) {
            if (fieldName.contains(Constants.DOT)) {
                String[] fieldNameParts = fieldName.split(RegTypes.DOT);
                if (fieldNameParts.length < 2) {
                    throw new ConditionGrammarException("condition.name malformed");
                }
                String addressFieldName = fieldNameParts[0];
                String addressFieldIndexStr = fieldNameParts[fieldNameParts.length - 1];
                Integer addressFieldIndex = ConditionDataConverter.tryConvertToInteger(addressFieldIndexStr);
                if (addressFieldIndex == null || addressFieldIndex.intValue() < 0) {
                    String msg = String.format("get param value failed, address index illegal, fieldName: %s",
                            fieldName);
                    throw new ConditionGrammarException(msg);
                }

                return this.paramsProxy.getAddress(addressFieldIndex, addressFieldName);
            } else {
                throw new ConditionGrammarException("condition.name for address dataType malformed");
            }
        }

        // 按字符串获取字段值
        paramValueOptional = this.paramsProxy.getStringValue(fieldName);
        if (!paramValueOptional.isPresent()) {
            return null;
        }

        switch (dataType) {
            case ConditionConstant.FieldTypes.STRING:
            case ConditionConstant.FieldTypes.SELECT:
            case ConditionConstant.FieldTypes.LEVEL:
            case ConditionConstant.FieldTypes.LABEL:
                return paramValueOptional.get();
            case ConditionConstant.FieldTypes.NUMBER:
                return ConditionDataConverter.convertToDouble(paramValueOptional.get());
            case ConditionConstant.FieldTypes.DATETIME:
                return ConditionDataConverter.convertToDate(paramValueOptional.get(), DateUtil.UTC_TIME_ZONE);
            default:
                return null;
        }
    }
}
